package com.jacky.iot.tasks;


import com.influxdb.client.InfluxDBClient;
import com.influxdb.client.WriteApi;
import com.influxdb.client.domain.WritePrecision;
import com.influxdb.client.write.Point;
import com.jacky.iot.utils.IPUtils;
import com.jacky.iot.utils.MathUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import oshi.SystemInfo;
import oshi.hardware.CentralProcessor;
import oshi.util.Util;

import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import java.util.TimerTask;
import java.util.concurrent.atomic.AtomicBoolean;

public class CpuUsageTask extends TimerTask {

    private final static Logger LOG = LoggerFactory.getLogger(CpuUsageTask.class);

    private final AtomicBoolean isFirstTime = new AtomicBoolean(true);
    private long[] lastPrevTicks;
    private long[][] lastCorePrevTicks;

    private final InfluxDBClient client;
    private final WriteApi writeApi;

    public CpuUsageTask(InfluxDBClient client) {
        this.client = client;
        writeApi = this.client.makeWriteApi();
    }

    @Override
    public void run() {
        try {
            String localIp = IPUtils.localIp();

            SystemInfo si = new SystemInfo();
            CentralProcessor cpu = si.getHardware().getProcessor();
            int logicalProcessorCount = cpu.getLogicalProcessorCount();

            String cpuType = cpu.getProcessorIdentifier().getName();

            // 如果是第一次收集则获取CPU ticks, 否则就以上一次收集的结束时间点数据作为本次的起始数据
            long[] prevTicks = isFirstTime.get() ? cpu.getSystemCpuLoadTicks() : lastPrevTicks;
            long[][] corePrevTicks = isFirstTime.get() ? cpu.getProcessorCpuLoadTicks() : lastCorePrevTicks;

            lastPrevTicks = prevTicks;

            // 第一次收集的时候需要等待1秒才能收集数据
            if (isFirstTime.get()) Util.sleep(1000);

            List<Point> batchPoints = new ArrayList<>();
            double totalUsage = cpu.getSystemCpuLoadBetweenTicks(prevTicks) * 100;

            long totalUser = 0;
            long totalSystem = 0;
            long totalIdle = 0;
            Instant now = Instant.now();
            long[][] ticksAll = cpu.getProcessorCpuLoadTicks();
            lastCorePrevTicks = ticksAll;
            isFirstTime.compareAndSet(true, false);
            for (int i = 0; i < logicalProcessorCount; i++) {
                long[] prevCoreTicks = corePrevTicks[i];
                long[] coreTicks = ticksAll[i];

                long user = coreTicks[CentralProcessor.TickType.USER.getIndex()]
                        - prevCoreTicks[CentralProcessor.TickType.USER.getIndex()];

                long system = coreTicks[CentralProcessor.TickType.SYSTEM.getIndex()]
                        - prevCoreTicks[CentralProcessor.TickType.SYSTEM.getIndex()];

                long idle = coreTicks[CentralProcessor.TickType.IDLE.getIndex()]
                        - prevCoreTicks[CentralProcessor.TickType.IDLE.getIndex()];

                long total = user + system + idle; // 加上其他所需的状态

                double usage = (total - idle) * 100.0 / total;
                totalUser += user;
                totalSystem += system;
                totalIdle += idle;

                Point point = Point.measurement("cpu_usage")
                        .time(now, WritePrecision.MS)
                        .addTag("cpu_core", "Core " + i)
                        .addTag("cpu_model", cpuType)
                        .addTag("host", localIp)
                        .addField("usage", MathUtils.doubleScale(usage, 1))
                        .addField("user", user)
                        .addField("idle", idle)
                        .addField("system", system);
                batchPoints.add(point);
            }
            Point totalPoint = Point.measurement("cpu_usage")
                    .time(now, WritePrecision.MS)
                    .addTag("cpu_core", "total")
                    .addTag("cpu_model", cpuType)
                    .addTag("host", localIp)
                    .addField("usage", MathUtils.doubleScale(totalUsage, 1))
                    .addField("user", totalUser)
                    .addField("idle", totalIdle)
                    .addField("system", totalSystem);
            batchPoints.add(totalPoint);

            writeApi.writePoints(batchPoints);
        } catch (Exception e) {
            LOG.error("收集OS数据异常:", e);
        }
    }
}
